package tiltmaze.solver;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import tiltmaze.Maze;

/**
 * Class used for generating a state graph of a maze.
 * @author Juraj Nižnan
 */
public class MazeStateGraph implements MazeStatesInterface {

    private int index = 1;
    private HashMap<MazePath, Integer> nodes = new HashMap<MazePath, Integer>();
    private ArrayList<MazeStateGraphEdge> edges = new ArrayList<MazeStateGraphEdge>();
    private ArrayList<MazeStateGraphEdge> deadlockEdges;
    private Maze maze;
    private BufferedWriter out;
    private String solverPaths;

    public MazeStateGraph(Maze maze) {
        this.maze = maze;
    }

    public void addEdge(MazePath p0, MazePath p1, int move) {
        edges.add(new MazeStateGraphEdge(nodes.get(p0), nodes.get(p1), "" + move));
    }

    public void addStartNode(MazePath startPath) {
        nodes.put(startPath, index++);
        edges.add(new MazeStateGraphEdge(0, nodes.get(startPath), ""));
    }

    public void addNode(MazePath p, boolean ended) {
        if (!nodes.keySet().contains(p)) {
            nodes.put(p, index++);
        }
        if (ended) {
            edges.add(new MazeStateGraphEdge(nodes.get(p), -1, ""));
        }
    }

    public void addDeadlockEdge(MazePath p0, int move) {
        if (deadlockEdges == null) {
            deadlockEdges = new ArrayList<MazeStateGraphEdge>();
        }
        deadlockEdges.add(new MazeStateGraphEdge(nodes.get(p0), -2, "" + move));
    }

    public void writeGML(String filename) throws IOException {
        out = new BufferedWriter(new FileWriter(filename));
        out.write("graph\n");
        out.write("[\n");
        out.write("hierarchic 1\n");
        out.write("label \"\"\n");
        out.write("directed 1\n");
        out.write("node\n[id 0 label \"S\"]\n");
        out.write("node\n[id -1 label \"W\"]\n");
        if (nodes.isEmpty()) {
            MazeSolver ms = new MazeSolver(maze);
            ms.stateGraph = this;
            ms.solve();
            solverPaths = ms.getPaths();
        }
        if (deadlockEdges != null) {
            out.write("node\n[id -2 label \"D\"]\n");
        }
        for (MazePath p : nodes.keySet()) {
            out.write("node\n[id " + nodes.get(p) + " label \"" + p.getStateNodeLabel() + "\"]\n");
        }
        for (MazeStateGraphEdge e : edges) {
            out.write("edge\n[source " + e.source + " target " + e.target + " label \"" + e.label + "\"]\n");
        }
        if (deadlockEdges != null) {
            for (MazeStateGraphEdge e : deadlockEdges) {
                out.write("edge\n[source " + e.source + " target " + e.target + " label \"" + e.label + "\"]\n");
            }
        }
        out.write("]");
        out.close();
    }

    /**
     * to be used only after writeGML
     */
    public String getSolverPaths() {
        return solverPaths;
    }
}

class MazeStateGraphEdge {

    public int source;
    public int target;
    public String label;

    public MazeStateGraphEdge(int source, int target, String label) {
        this.source = source;
        this.target = target;
        this.label = label;
    }
}
